﻿using GenericRepositoryUnitOfWorkDemo.Domain.Abstracts.UnitOfWorks;
using Microsoft.Extensions.Options;
using System.Transactions;

namespace GenericRepositoryUnitOfWorkDemo.Infrastructure.Abstracts.UnitOfWorks;

public class UnitOfWorkManager : IUnitOfWorkManager
{
    private readonly IUnitOfWork _unitOfWork;

    public UnitOfWorkManager(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public IUnitOfWork Begin()
    {
        var outerUow = Current;

        if (outerUow != null)
        {
            return new UnitOfWorkInner(outerUow);
        }

        var uow = _unitOfWork;
        uow.BeginTransaction();

        Current = uow;

        return uow;
    }

    public IUnitOfWork Current
    {
        get { return GetCurrentUow(); }
        set { SetCurrentUow(value); }
    }
    private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>();
    private static IUnitOfWork GetCurrentUow()
    {
        var uow = AsyncLocalUow.Value?.UnitOfWork;
        if (uow == null)
        {
            return null;
        }

        return uow;
    }
    private static void SetCurrentUow(IUnitOfWork value)
    {
        lock (AsyncLocalUow)
        {

            if (AsyncLocalUow.Value?.UnitOfWork == null)
            {
                if (AsyncLocalUow.Value != null)
                {
                    AsyncLocalUow.Value.UnitOfWork = value;
                }

                AsyncLocalUow.Value = new LocalUowWrapper(value);
                return;
            }

            AsyncLocalUow.Value.UnitOfWork = value;
        }
    }
    private class LocalUowWrapper
    {
        public IUnitOfWork UnitOfWork { get; set; }

        public LocalUowWrapper(IUnitOfWork unitOfWork)
        {
            UnitOfWork = unitOfWork;
        }
    }
}